/**
 * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 */

package com.iwindplus.boot.web.mvc.handler;

import cn.hutool.core.collection.CollUtil;
import com.iwindplus.boot.domain.vo.ResultVO;
import com.iwindplus.boot.util.CryptoUtil;
import com.iwindplus.boot.util.JacksonUtil;
import com.iwindplus.boot.web.domain.property.ResponseBodyProperty;
import com.iwindplus.boot.web.domain.property.ResponseBodyProperty.ResponseBodyCryptoConfig;
import jakarta.annotation.Resource;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * 统一响应体增强处理器.
 *
 * @author zengdegui
 * @since 2021/11/4
 */
@Slf4j
@RestControllerAdvice
@EnableConfigurationProperties({ResponseBodyProperty.class})
@ConditionalOnProperty(prefix = "response.body", name = "enabled", havingValue = "true", matchIfMissing = true)
public class ResponseBodyHandler implements ResponseBodyAdvice<Object> {

    @Resource
    private ResponseBodyProperty responseBodyProperty;

    private static final List<String> IGNORED_CLASSES = List.of(
        "SwaggerConfigResource",
        "OpenApiWebMvcResource",
        "MultipleOpenApiWebMvcResource"
    );

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> converterType) {
        Method method = methodParameter.getMethod();
        String simpleName = method.getDeclaringClass().getSimpleName();

        List<String> ignoredClasses = this.buildIgnoredClasses();
        return !ignoredClasses.contains(simpleName) && this.responseBodyProperty.getEnabled();
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass,
        ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        if (Objects.nonNull(body) && Objects.nonNull(mediaType) && mediaType.includes(MediaType.APPLICATION_JSON)) {
            ResultVO<?> data;
            if (body instanceof ResultVO<?> result) {
                data = result;
            } else {
                data = ResultVO.success(body);
            }
            // 判断是否启用响应加密
            final ResponseBodyCryptoConfig crypto = responseBodyProperty.getCrypto();
            if (Boolean.TRUE.equals(responseBodyProperty.getEnabled()) && Boolean.TRUE.equals(crypto.getEnabled())) {
                final String param = JacksonUtil.toJsonStr(data);
                return CryptoUtil.encrypt(param, crypto);
            }
            return data;
        }
        return body;
    }

    private List<String> buildIgnoredClasses() {
        List<String> urls = Lists.newArrayList();
        urls.addAll(IGNORED_CLASSES);
        List<String> ignoredClasses = this.responseBodyProperty.getIgnoredClasses();
        if (CollUtil.isNotEmpty(ignoredClasses)) {
            urls.addAll(ignoredClasses);
        }
        return urls.stream().distinct().collect(Collectors.toCollection(ArrayList::new));
    }
}
